package com.hyt.it.ogt.data.service.pj.impl;

import java.math.BigDecimal;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hyt.core.base.BaseServiceImpl;
import com.hyt.core.util.FastJsonUtil;
import com.hyt.core.util.UUIDUtils;
import com.hyt.exception.exception.BusinessLogicException;
import com.hyt.it.ogt.common.utils.DateTimeUtil;
import com.hyt.it.ogt.common.utils.ExecutorPoolUtil;
import com.hyt.it.ogt.data.Constants;
import com.hyt.it.ogt.data.PjIncrementDataDistribution;
import com.hyt.it.ogt.data.enums.CommonEnums;
import com.hyt.it.ogt.data.mapper.pj.PjDataProjectMapper;
import com.hyt.it.ogt.data.model.dto.StudentExtendDTO;
import com.hyt.it.ogt.data.model.entity.pj.ItemScore;
import com.hyt.it.ogt.data.model.entity.pj.Piece;
import com.hyt.it.ogt.data.model.entity.pj.PieceScore;
import com.hyt.it.ogt.data.model.entity.pj.Project;
import com.hyt.it.ogt.data.model.entity.pj.Student;
import com.hyt.it.ogt.data.model.entity.pj.StudentAnswer;
import com.hyt.it.ogt.data.model.entity.pj.Subject;
import com.hyt.it.ogt.data.model.entity.pj.Task;
import com.hyt.it.ogt.data.model.vo.kw.ProjectVO;
import com.hyt.it.ogt.data.model.vo.pj.ObjectiveScoreVO;
import com.hyt.it.ogt.data.model.vo.pj.PaperDataVO;
import com.hyt.it.ogt.data.model.vo.pj.StudentAnswerVO;
import com.hyt.it.ogt.data.model.vo.pj.SubjectScore;
import com.hyt.it.ogt.data.service.kw.IExamCandidateService;
import com.hyt.it.ogt.data.service.kw.IExamPaperService;
import com.hyt.it.ogt.data.service.pj.IPjDataCommonAutomaticAllocationService;
import com.hyt.it.ogt.data.service.pj.IPjDataConfigService;
import com.hyt.it.ogt.data.service.pj.IPjDataExamExpertService;
import com.hyt.it.ogt.data.service.pj.IPjDataItemScoreService;
import com.hyt.it.ogt.data.service.pj.IPjDataPaperService;
import com.hyt.it.ogt.data.service.pj.IPjDataPieceItemService;
import com.hyt.it.ogt.data.service.pj.IPjDataPieceScoreService;
import com.hyt.it.ogt.data.service.pj.IPjDataPieceService;
import com.hyt.it.ogt.data.service.pj.IPjDataProjectService;
import com.hyt.it.ogt.data.service.pj.IPjDataStudentAnswerService;
import com.hyt.it.ogt.data.service.pj.IPjDataStudentService;
import com.hyt.it.ogt.data.service.pj.IPjDataSubjectScoreService;
import com.hyt.it.ogt.data.service.pj.IPjDataSubjectService;
import com.hyt.it.ogt.data.service.pj.IPjDataTaskService;
import com.hyt.it.ogt.pj.common.ResponseCode;
import com.hyt.it.ogt.pj.model.entity.Config;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * <p>
 * 评卷项目表 服务实现类
 * </p>
 *
 * @author liuqi
 * @date 2022-06-02
 */
@Service
@Slf4j
public class PjDataProjectServiceImpl extends BaseServiceImpl<PjDataProjectMapper, Project> implements IPjDataProjectService {

    @Resource
    RedissonClient redissonClient;

    /*************************************pj*********************************************/

    @Resource
    private IPjDataConfigService iPjDataConfigService;

    @Resource
    private IPjDataSubjectService iPjDataSubjectService;

    @Resource
    private IPjDataPaperService iPjDataPaperService;

    @Resource
    private IPjDataStudentService iPjDataStudentService;

    @Resource
    private IPjDataStudentAnswerService iPjDataStudentAnswerService;

    @Resource
    private IPjDataPieceService iPjDataPieceService;

    @Resource
    private IPjDataPieceScoreService pieceScoreService;

    @Resource
    private IPjDataPieceItemService pieceItemService;

    @Resource
    private IPjDataSubjectScoreService iPjDataSubjectScoreService;

    @Resource
    private IPjDataTaskService taskService;

    @Resource
    private IPjDataCommonAutomaticAllocationService commonAutomaticAllocationService;

    @Autowired
    private PjIncrementDataDistribution pjIncrementDataDistribution;

    @Resource
    private IPjDataItemScoreService iPjDataItemScoreService;

    /**************************************kw *********************************************/

    @Resource
    private IExamCandidateService iExamCandidateService;

    @Resource
    private IExamPaperService iExamPaperService;

    @Resource
    private IPjDataExamExpertService examExpertService;

    @Override
    public void initProjectForTask(String projectId, String officeId, String deptId) {
        
        // 1.保存评卷项目信息
        log.info("[推送评卷] initProjectForTask 初始化项目信息");
        this.saveProject(projectId);
        
        // 2. 初始化评卷考试的试卷信息
        log.info("[推送评卷] initProjectForTask 初始化试卷信息");
        List<PaperDataVO> papers = iExamPaperService.getPaperByProject(projectId);
        if (CollectionUtils.isEmpty(papers)) {
            throw new BusinessLogicException(ResponseCode.INIT_PROJECT_NO_PAPER, "初始化项目时没有读取到试卷");
        }
        iPjDataPaperService.savePaper(projectId, papers);
        
        // 3. 初始化评卷考试的科目信息
        log.info("[推送评卷] initProjectForTask 初始化科目信息");
        List<Subject> subjectsTemp = iExamCandidateService.getExamSubject(projectId);
        if (CollectionUtils.isEmpty(subjectsTemp)) {
            throw new BusinessLogicException(ResponseCode.INIT_PROJECT_NO_SUBJECT, "初始化项目时没有读取到科目数据");
        }
        // 过滤掉没有试卷的科目信息
        List<Subject> subjects = delNoPaperItemsPapers(papers, subjectsTemp);
        iPjDataSubjectService.saveSubject(projectId, subjects);
        
        // 4.初始化评卷参数
        log.info("[推送评卷] initProjectForTask 初始化评卷参数信息");
        iPjDataConfigService.initMarkParam(projectId);
        
        // 5.初始化piece信息
        log.info("[推送评卷] initProjectForTask 初始化评卷块piece信息");
        iPjDataPieceService.initPiece(projectId, subjects);
    }

    @Override
    public void addProjectStudentForTask(String projectId, String officeId, String admissionNum) {
        // 1.重复的考生推送先把上一次撤销
        List<Student> studentList = iPjDataStudentService.lambdaQuery().eq(Student::getProjectId, projectId).eq(Student::getAdmissionNum, admissionNum).list();
        if (CollUtil.isNotEmpty(studentList)) {
            cancelCandidateTask(projectId, admissionNum);
        }

        // 2.考务查询考生信息
        List<StudentExtendDTO> studentExtendDTOs = iExamCandidateService.getCandidateInfo(projectId, admissionNum);

        // 3. 考务查询考生答案
        List<StudentAnswerVO> studentAnswerVOs = iExamCandidateService.getCandidateAnswer(projectId, admissionNum);
        if (CollectionUtils.isEmpty(studentAnswerVOs)) {
            throw new BusinessLogicException(ResponseCode.INIT_PROJECT_NO_STUDENT_ANSWER, "初始化数据时没有读取到考生答案");
        }

        // 4. 考务查询客观题分数
        List<ObjectiveScoreVO> objectiveScoreVOs = iExamCandidateService.getObjectiveScore(projectId, admissionNum);

        // 5. 评卷保存考生信息
        iPjDataStudentService.saveStudent(projectId, studentExtendDTOs);

        // 6. 评卷保存考生答案
        iPjDataStudentAnswerService.saveStudentAnswer(projectId, studentAnswerVOs);

        // 7. 评卷保存科目客观题分数
        iPjDataSubjectScoreService.saveObjectiveScore(projectId, objectiveScoreVOs, iPjDataSubjectService.selectByProjectId(projectId));

        // 8. 评卷初始化评卷任务
        initTaskInfo(projectId, admissionNum, studentExtendDTOs);

        // 9.考务标记考生
        iExamCandidateService.markCandidate(projectId, admissionNum);

        // 10. 自动分配评卷任务
        incrementDataDistribution(projectId);
    }

    /**
     * 重复的考生推送先把上一次撤销
     *
     * @param projectId
     * @param admissionNum
     */
    private void cancelCandidateTask(String projectId, String admissionNum) {

        //1、减少科目表中的实考人数(pj_subject)
        iPjDataStudentService.reduceExamNum(projectId, admissionNum);

        //2、删除考生表中对应需要撤销评卷的考生(pj_student)
        iPjDataStudentService.remove(new LambdaQueryWrapper<Student>().eq(Student::getProjectId, projectId).eq(Student::getAdmissionNum, admissionNum));

        //3、考生答案表中删除对应需要撤销评卷的考生答案(pj_student_answer)
        iPjDataStudentAnswerService.remove(new LambdaQueryWrapper<StudentAnswer>().eq(StudentAnswer::getProjectId, projectId).eq(StudentAnswer::getAdmissionNum, admissionNum));

        //4、考生客观分表中删除对应需要撤销评卷的考生客观分(pj_subject_score)
        iPjDataSubjectScoreService.remove(new LambdaQueryWrapper<SubjectScore>().eq(SubjectScore::getProjectId, projectId).eq(SubjectScore::getAdmissionNum, admissionNum));


        List<Task> taskList = taskService.lambdaQuery().eq(Task::getProjectId, projectId).eq(Task::getAdmissionNum, admissionNum).list();
        if (CollectionUtil.isEmpty(taskList)) {
            log.info("[推送评卷]>>>>>>>>>>> 撤销交卷查询task.size is null :{},{}", admissionNum, projectId);
            return;
        }

        log.info("[推送评卷]>>>>>>>>>>> 撤销交卷：admissionNum:{},projectId:{}", admissionNum, projectId);
        List<String> taskIdList = taskList.stream().map(item -> item.getId()).collect(Collectors.toList());


        // 5、查询需要被删除的试题id
        List<String> itemIdList = iPjDataItemScoreService.lambdaQuery().eq(ItemScore::getEncodeId, admissionNum)
                .in(ItemScore::getTaskId, taskIdList).list().stream().map(ItemScore::getItemId)
                .collect(Collectors.toList());

        // 6、删除考生小题分数(pj_item_score)
        iPjDataItemScoreService.remove(new LambdaQueryWrapper<ItemScore>().eq(ItemScore::getEncodeId, admissionNum)
                .in(ItemScore::getTaskId, taskIdList)
                .in(CollectionUtil.isNotEmpty(itemIdList), ItemScore::getItemId, itemIdList));

        // 7、删除pieceScore
        pieceScoreService.remove(new LambdaQueryWrapper<PieceScore>().eq(PieceScore::getEncodeId, admissionNum)
                .in(PieceScore::getTaskId, taskIdList));

        // 8、通过id删除评卷任务(pj_task)
        taskService.remove(new LambdaQueryWrapper<Task>().in(Task::getId, taskIdList));
    }

    /**
     * 移除掉没有主观题的试卷
     *
     * @param paperTemps
     * @param subjects
     * @return
     */
    private List<Subject> delNoPaperItemsPapers(List<PaperDataVO> paperTemps, List<Subject> subjects) {
        List<String> unRemoveSubjectIds = new ArrayList<String>();
        for (Iterator<PaperDataVO> iterator = paperTemps.iterator(); iterator.hasNext(); ) {
            PaperDataVO paperDataVO = iterator.next();
            unRemoveSubjectIds.add(paperDataVO.getSubjectId());
        }
        if (CollUtil.isNotEmpty(unRemoveSubjectIds)) {
            for (Iterator<Subject> iterator = subjects.iterator(); iterator.hasNext(); ) {
                Subject subject = iterator.next();
                if (!unRemoveSubjectIds.contains(subject.getSubjectId())) {
                    iterator.remove();
                }
            }
        }
        return subjects;
    }

    private void incrementDataDistribution(String pjProjectId) {
        // 将评分数据同步到成绩系统
        pjIncrementDataDistribution.setPjProjectId(pjProjectId);
        pjIncrementDataDistribution.setiCommonAutomaticAllocationService(commonAutomaticAllocationService);
        // 异步形成执行批处理操作，可能数据量比较大
        ExecutorPoolUtil.getExecutorService().execute(pjIncrementDataDistribution);
    }

    /**
     * 保存评卷项目信息
     *
     * @param projectId
     * @param deptId
     */
    private void saveProject(String projectId) {
        // 1.初始化项目
        if (projectId == null) {
            throw new BusinessLogicException(ResponseCode.INIT_PROJECTID_NULL, "初始化项目时传入的项目id为空");
        }
        Project project = this.getById(projectId);
        if (project != null && project.getIsInit()) {
            // 1.1已經初始化過了
            throw new BusinessLogicException(ResponseCode.INIT_PROJECT_EXIST_ERROR, "项目已经初始化过");
        }
        // 1.2直接查数据库获取项目相关信息，并插入数据库
        project = this.getProjectById(projectId);
        project.setIsInit(true);
        project.setVersion(CommonEnums.PROJECT_VERSION_1.getCode());
        // 如果是无考场数据
        if (CommonEnums.EXAM_CLASSFICATION_0.getCode().equals(project.getExamClassfication())) {
            project.setTaskName((project.getProjectName()));
            project.setTaskId(project.getId());
            project.setTaskBeginDate(project.getBeginDate());
            project.setTaskEndDate(project.getEndDate());
        }
        this.save(project);
    }

    /**
     * 从kw库查项目信息(原来是通过kwClient查询)
     *
     * @param projectId
     */
    private Project getProjectById(String projectId) {
        ProjectVO kwProjectInfo = iExamPaperService.getProjectInfo(projectId);
        Project pjProject = new Project();
        BeanUtils.copyProperties(kwProjectInfo, pjProject);
        pjProject.setBeginDate(DateTimeUtil.parseDate(kwProjectInfo.getBeginDate()));
        pjProject.setEndDate(DateTimeUtil.parseDate(kwProjectInfo.getEndDate()));
        pjProject.setTaskBeginDate(DateTimeUtil.asDate(kwProjectInfo.getTaskBeginDate()));
        pjProject.setTaskEndDate(DateTimeUtil.asDate(kwProjectInfo.getTaskEndDate()));
        pjProject.setOfficeId(kwProjectInfo.getOfficeId());
        pjProject.setDeptId(kwProjectInfo.getDeptId());
        return pjProject;
    }

    /**
     * 初始化评卷结构
     *
     * @param projectId 评卷项目id
     * @param officeId  用户id
     * @param admission 准考证号
     */
    private void initTaskInfo(String projectId, String admission, List<StudentExtendDTO> studentExtendDTO) {
        // 1.读取参数查看评卷模式，默认为整卷模式
        Config config = iPjDataConfigService.selectByLabel(projectId, Constants.MARK_MODEL);
        // 如果是整卷评卷
        if (config != null && "A".equals(config.getValue())) {
            // 整卷评卷
            markPaperIncrment(projectId, admission, studentExtendDTO);
        } else if (config != null && "I".equals(config.getValue())) {
            // 按题评卷，暂不实现
        }
    }

    /**
     * 增量任务下初始化评卷结构
     *
     * @param projectId 评卷项目id
     * @param officeId  用户id
     * @param admission 准考证号
     */
    private void markPaperIncrment(String projectId, String admission, List<StudentExtendDTO> studentExtendDTOS) {
        // 组装task需要的数据
        Map<String, StudentExtendDTO> studentExtendMap = new HashMap<>();
        for (StudentExtendDTO item : studentExtendDTOS) {
            StringBuilder key = new StringBuilder();
            key.append(item.getProjectId());
            key.append(item.getSubjectId());
            key.append(item.getAdmissionNum());
            studentExtendMap.put(key.toString(), item);
        }
        List<Subject> subjects = iPjDataSubjectService.selectByProjectId(projectId);
        // 生成piece、pieceItem、task、teacherTaskInfo
        log.info("[推送评卷] markPaperIncrment subjects:{}", FastJsonUtil.getBeanToJson(subjects));
        for (Subject subject : subjects) {
            incrementTask(projectId, subject.getSubjectId(), admission, studentExtendMap);
        }
    }

    /**
     * 生成增量评卷任务
     *
     * @param projectId    评卷项目id
     * @param subjectId    评卷科目
     * @param officeId     当前用户
     * @param admissionNum 增量考生的准考证号
     */
    private void incrementTask(String projectId, String subjectId, String admissionNum, Map<String, StudentExtendDTO> studentExtendMap) {
        List<Task> existTasks = taskService.getStudentSubjectTask(projectId, subjectId, admissionNum);
        if (CollUtil.isEmpty(existTasks)) {
            List<Piece> pieces = iPjDataPieceService.lambdaQuery().eq(Piece::getProjectId, projectId).eq(Piece::getSubjectId, subjectId).eq(Piece::getDelFlag, false).list();
            Student student = iPjDataStudentService.getByAdmissionNum(projectId, subjectId, admissionNum);
            // 由于外面循环的科目是全量的，AB卷可能会出现null的情况，return即可
            if (null == student) {
                return;
            }
            List<Task> tasks = new ArrayList<>();
            for (Piece piece : pieces) {
                boolean paperPiece = CommonEnums.PIECE_TYPE_2.getCode().equals(piece.getPieceType());
                boolean paperIdEqual = piece.getPackageId() == null && piece.getPaperId() != null && piece.getPaperId().equals(student.getPaperId());
                boolean packageIdEqual = piece.getPackageId() != null && piece.getPackageId().equals(student.getPackageId()) && piece.getPackageVersion().equals(student.getPackageVersion());
                if (paperPiece && (paperIdEqual || packageIdEqual)) {
                    Task task = new Task();
                    task.setId(UUIDUtils.newSortUUID());
                    task.setProjectId(projectId);
                    task.setSubjectId(subjectId);
                    task.setAdmissionNum(admissionNum);
                    task.setEncodeId(admissionNum);
                    task.setPieceId(piece.getId());
                    task.setStage(Constants.MARK_STAGE);
                    task.setStatus(0);
                    // task 其他数据
                    StringBuilder sb = new StringBuilder();
                    sb.append(projectId);
                    sb.append(subjectId);
                    sb.append(admissionNum);
                    StudentExtendDTO studentExtendDTO = studentExtendMap.get(sb.toString());
                    extendTask(task, studentExtendDTO);
                    tasks.add(task);
                    iPjDataPieceService.updateFinishStatus(projectId, subjectId, piece.getId(), false);
                }
            }
            taskService.saveBatch(tasks);
            if (CollUtil.isNotEmpty(tasks)) {
                iPjDataSubjectService.updateSubjectMarkStatus(projectId, subjectId, false);
            }
        }
    }

    /**
     * 设置task相关考点考场等数据
     *
     * @param task
     * @param studentExtendDTO
     */
    private void extendTask(Task task, StudentExtendDTO studentExtendDTO) {
        // 设置task里的值
        if (null != studentExtendDTO) {
            task.setPlaceId(studentExtendDTO.getPlaceId());
            task.setPlaceName(studentExtendDTO.getPlaceName());
            task.setRoomId(studentExtendDTO.getRoomId());
            task.setRoomName(studentExtendDTO.getRoomName());
            task.setSeatNo(studentExtendDTO.getSeatNo());
            // 考试开始时间
            if (null != studentExtendDTO.getExamBeginDate()) {
                task.setExamStartTime(Date.from(studentExtendDTO.getExamBeginDate().atZone(ZoneId.systemDefault()).toInstant()));
            }
            // 考试结束时间
            if (null != studentExtendDTO.getExamEndDate()) {
                task.setExamEndTime(Date.from(studentExtendDTO.getExamEndDate().atZone(ZoneId.systemDefault()).toInstant()));
            }
            // 考生答题时长
            if (null != studentExtendDTO.getSubjectUseTime()) {
                task.setAnswerTime(new BigDecimal(studentExtendDTO.getSubjectUseTime() / 1000));
            }
        }
        Project project = this.baseMapper.selectById(task.getProjectId());
        // 虚拟考场的设置批次名称和批次id
        if (null != project.getExamClassfication() && CommonEnums.EXAM_CLASSFICATION_1.getCode().equals(project.getExamClassfication())) {
            task.setBatchId(project.getId());
            task.setBatchName(project.getProjectName());
        }
    }

    @Override
    public void updateProjectForTask(String projectId, String officeId) {
        // 1.初始化项目
        if (projectId == null) {
            return;
        }
        Project project = this.getById(projectId);
        if (project != null) {
            // 1.2直接查数据库获取项目相关信息，并插入数据库
            Project kwProject = this.getProjectById(projectId);
            if (CommonEnums.EXAM_CLASSFICATION_0.getCode().equals(project.getExamClassfication())) {
                project.setProjectName(kwProject.getProjectName());
                project.setTaskId(kwProject.getId());
                project.setTaskName(kwProject.getProjectName());
                project.setTaskBeginDate(kwProject.getBeginDate());
                project.setTaskEndDate(kwProject.getEndDate());
            }
            this.baseMapper.updateById(project);
        }
    }
}
